#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <sys/socket.h>
#include <errno.h>

#define PORT 8888
#define QUIT_STR "quit"

int main(int argc, const char * argv[]) 
{
    //1.创建监听的套接字
    int lfd = socket(AF_INET, SOCK_STREAM, 0);
    if (lfd == -1)
    {
	perror("socket error\n");
	exit(1);
    }

    //使用setsockopt实现地址快速复用
    int b_reuse = 1;
    setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &b_reuse, sizeof(int));

    //使用setsockopt实现10秒网络超时检测
    struct timeval time_out;
    time_out.tv_sec = 10;
    time_out.tv_usec = 0;
    int rec = setsockopt(lfd, SOL_SOCKET, SO_RCVTIMEO, &time_out, sizeof(time_out));
    if(rec == -1)
    {
	perror("setsocket");
	exit(1);
    }


    //2.lfd和本地的IP和port绑定
    struct sockaddr_in server;
    memset(&server, 0, sizeof(server));
    server.sin_family = AF_INET;  //地址族协议  ipv4
    server.sin_port = htons(PORT); //端口号
    server.sin_addr.s_addr = htonl(INADDR_ANY); //ip地址
    int ret = bind(lfd, (struct sockaddr*)&server, sizeof(server));
    if (ret == -1)
    {
	perror("bind error\n");
	exit(1);
    }


    //3.设置监听
    ret = listen(lfd, 20);
    if (ret == -1)
    {
	perror("listen error\n");
	exit(1);
    }

    //4.等待并接受连接请求
    struct sockaddr_in client;
    socklen_t len = sizeof(client);
    while(1)
    {
	int cfd = accept(lfd, (struct sockaddr*)&client, &len);
	if (cfd == -1)
	{
	    if (errno == EAGAIN || errno == EWOULDBLOCK)
	    {    
		printf("accept timeout\n");            
		continue;
	    }
	    else
	    {    
		perror("accept error");
		exit(1);
	    }
	}
	//获取客户端数据
	printf("accept successful\n");
	char ipbuf[64] = {0};
	inet_ntop(AF_INET, &client.sin_addr.s_addr, ipbuf, sizeof(ipbuf)); //获取客户端ip
	printf("client IP:%s, port:%d\n", ipbuf, ntohs(client.sin_port));
	//接收数据
	while (1)
	{
	    char buf[1024] = {0};
	    int len = read(cfd, buf, sizeof(buf));
	    if(len == -1)
	    {
		if (errno == EAGAIN || errno == EWOULDBLOCK)
		{    
		    printf("read timeout\n");            
		}
		else
		{    
		    perror("read error");
		    exit(1);
		}
	    }
	    else if (len == 0)
	    {
		close(cfd);
		break;
	    }
	    else
	    {
		printf("recv buf: %s", buf);
		if (!strncasecmp(buf, QUIT_STR, strlen(QUIT_STR))) 
		{
		    printf("client is exiting\n");
		    close(cfd);
		    break;
		}
	    }
	}
    }

    close(lfd);
    return 0;
}    
